/** 
 * 公开任务（Public tasks） 从 gulpfile 中被导出（export），可以通过 gulp 命令直接调用。
 * 私有任务（Private tasks） 被设计为在内部使用，通常作为 series() 或 parallel() 组合的组成部分。
 * 在以前的 gulp 版本中，task() 方法用来将函数注册为任务（task）。虽然这个 API 依旧是可以使用的，但是 导出（export）将会是主要的注册机制，除非遇到 export 不起作用的情况。
 * 
 * 串行任务 
 * gulp.series('xxx','xxx');
 * 并行任务
 * gulp.parallel('xxx','xxx');
 * */

const _port = 38900;

const minimist = require('minimist') //命令行传递参数
const options = minimist(process.argv.slice(2))
    ? minimist(process.argv.slice(2))
    : '' //命令行传递参数 options   gulp xxxx ----xxx  ----xxx就是传参方式



const gulp = require('gulp');
const ts = require('gulp-typescript'); // ts 转es5
const tsConfig = ts.createProject('./tsconfig.json') //typescript配置文件
const rev = require('gulp-rev'); // 打版本  把当前文件添加版本号
const open = require('gulp-open'); // 自动打开浏览器
const sass = require('gulp-sass'); // 预编译scss  需要提前下载 node-sass
const clean = require('gulp-clean'); // 清空文件
const babel = require('gulp-babel'); // es6转换
const mkdirp = require('mkdirp') // 创建文件任务
const uglify = require('gulp-uglify'); // js压缩
const concat = require('gulp-concat'); // 合并文件
const connect = require('gulp-connect'); // 启动测试服务
const htmlmin = require('gulp-htmlmin'); // 压缩html
const imagemin = require('gulp-imagemin');// 压缩图片
const cleanCss = require('gulp-clean-css'); // 压缩css
const browserify = require('gulp-browserify'); // 自动处理模块依赖 解决babel转es7 require is not defined的问题
const sourcemaps = require('gulp-sourcemaps'); // 用记录代码的源位置 开发阶段用到
const modRewrite = require('connect-modrewrite'); // 服务器代理
const autoprefixer = require('gulp-autoprefixer'); // 自动打前缀
const revCollector = require('gulp-rev-collector'); // 管理版本替换
const watch = require('gulp-watch'); // 解决gulp.watch无法监听新增文件的问题

/****************目录常量配置*********************/

const files = {
    main: options['fileName'] ? options['fileName'] : '' + './src/',
    outPut: './dist/',
    src: {
        //源文件目录结构 也可以用于创建目录
        html: 'html/',          // html源文件目录
        style: 'style/',        // 样式源文件目录
        lib: 'lib/',            // 第三方源文件目录
        js: 'js/',              // JS源文件目录(es5)
        rev: 'rev/',
        es6: 'es6/',            // JS源文件目录(es6) 不能改动 改动只能去es6改 因为这里是转译的文件
        assets: 'assets/',      // 图片源文件目录
        comp: 'comp/',          // 组件源文件目录
        ts: 'ts/'               // typeScript源文件目录
    },
    dist: {
        //生产环境目录
        html: 'html/',
        style: 'style/',
        lib: 'lib/',
        js: 'js/',
        es6: 'es6/',
        rev: 'rev/',
        assets: 'assets/',
        comp: 'comp/'
    },
    rev: {}
}

function swallowError(error) {
    // If you want details of the error in the console
    console.error(error.toString())
    this.emit('end')
}

// 创家目录任务
function createFile(cb) {
    let oName = options['fileName'] ? options['fileName'] + '/' : 'src/';
    if (oName) {
        if (typeof oName !== 'string') {
            console.log('项目文件夹名称，只能是字符串')
            return
        }
    }

    mkdirp.sync(oName);

    for (let i in files['src']) {
        if (i === 'style') {
            mkdirp.sync(oName + files['src'][i] + '/' + 'css');
            mkdirp.sync(oName + files['src'][i] + '/' + 'scss');
            mkdirp.sync(oName + files['src'][i] + '/' + 'less');
            mkdirp.sync(oName + files['src'][i] + '/' + 'stylus');
        } else if (i === 'assets') {
            mkdirp.sync(oName + files['src'][i] + '/' + 'img');
            mkdirp.sync(oName + files['src'][i] + '/' + 'font');
            mkdirp.sync(oName + files['src'][i] + '/' + 'media');
        }
        //  else if (i === 'js') {
        //     mkdirp.sync(oName + files['src'][i] + '/' + 'es5');
        //     mkdirp.sync(oName + files['src'][i] + '/' + 'es6');
        // }
        else {
            mkdirp.sync(oName + files['src'][i])
        }
    }
    cb();
}

function devSever(cb) {
    connect.server({
        root: ['src'],        // 网站根目录
        port: _port,            // 端口
        livereload: true,       // 热重载
        middleware: (connect, opt) => { // 设置代理
            return [
                modRewrite([
                    '^/api/(.*)$ http://xxx.com'
                ])
            ]
        }
    })
    cb();
}

function openBrowser(cb) {
    // 'google-chrome' // Linux
    // 'chrome' // Windows
    // 'google chrome' or 'Google Chrome' // OSX
    // 'firefox'
    gulp.src(__filename)
        .pipe(open({
            uri: `http://localhost:${_port}/html/index.html`,
            app: 'google chrome'
        }))
    cb();
}

function reload(cb) {
    gulp.src(__filename)
        .pipe(connect.reload())
    cb();
}

function cleanFile(srcArr = []) {
    let arr = [
        `${files['outPut']}`,
    ];

    if (srcArr.length) {
        arr = arr.concat(src);
    }

    return gulp.src(arr, { read: false })
        .pipe(clean());
}

// 样式预处理把 (目前只处理了scss)
// 1. scss -> css -> 合并成main.css
// 2. 添加前缀
function style(cb) {
    // 把scss 转换成css
    let _import = `${files['main']}${files['src']['style']}{css,scss}/**/*.{css,scss}`;
    let _output = `${files['main']}style`;

    gulp
        .src(
            [_import, `!${files['main']}${files['src']['style']}main.css`]
        )
        .pipe(sourcemaps.init())
        .pipe(autoprefixer({
            overrideBrowserslist: ['> 0.1% in CN'], // 支持浏览器的版本
            cascade: false // 样式格式
        }))
        .pipe(sass().on('error', sass.logError)) // 抛出错误在终端而不是直接中断
        .pipe(concat('main.css'))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest(_output))
        .pipe(connect.reload())
    cb();
}

// 把css的文件都进行合并成一个main.css
// 1. 压缩css
// 2. 打版本号
function stylePro(cb) {
    let _import = `${files['main']}${files['src']['style']}css/**/*.css`;
    let _output = `${files['outPut']}style`
    let revOutput = `${files['main']}rev/css`;

    // console.log(_import)
    // console.log(_output)

    return gulp
        .src(
            [_import]
        )
        .pipe(autoprefixer({
            overrideBrowserslist: ['last 2 versions'], // 支持浏览器的版本
            cascade: false // 样式格式
        }))
        .pipe(concat('main.css'))
        .pipe(cleanCss({// 压缩css
            // 兼容IE8
            compatibility: 'ie8',
            // 保留前缀避免删除
            keepSpecialComments: '*'
        }))
        .pipe(rev()) // 给main.css 生成版本映射
        .pipe(gulp.dest(_output))
        .pipe(rev.manifest()) // 生成映射文件
        .pipe(gulp.dest(revOutput))
}

// 把src/html/下的所有html 复制到dist目录 并且替换css js的版本替换
// html压缩
function html(cb) {
    // 
    let htmlSrc = `${files['main']}${files['src']['html']}**/*.html`;
    let manifestJsonSrc = `${files['main']}${files['src']['rev']}rev-manifest.json`;
    let _output = `${files['outPut']}`
    console.log(manifestJsonSrc)
    return gulp
        .src(
            [htmlSrc, manifestJsonSrc],
            { base: `${files['main']}` }
        )
        .pipe(revCollector({ replaceReved: true }))
        .pipe(htmlmin({
            removecomments: true, //清除HTML注释
            collapseWhitespace: true, //压缩HTML
            // collapseBooleanAttributes: true, //省略布尔属性的值<input checked="true"/> ==> <input />
            removeEmptyAttributes: true, //删除所有空格作属性值<input id=""/> ==> <input />
            removeScriptTypeAttributes: true, //删除<script>的type="text/javascript"
            removeStyleLinkTypeAttributes: true, //删除<style>和<link>的type="text/css"
            minifyJS: true, //压缩页面JS
            minifycsS: true //压缩页面CsS
        }))// 压缩html
        .pipe(gulp.dest(_output))
}

// 压缩assets/img文件夹下的文件
function imgMini(cb) {
    let imageSrc = `${files['main']}${files['src']['assets']}img/**/*.{png,jpg,jpeg,gif,ico,svg}`;
    let _output = `${files['outPut']}assets/img/`
    console.log(imageSrc)
    return gulp.src(imageSrc)
        .pipe(imagemin({
            optimizationLevel: 5, //类型: Number 默认: 3 取值范围: 0-7 (优化等级)progressiye: true, //类型: Boolean默认: false无损压缩jpg图片
            interlaced: true,
            //类型: Boolean默认: false隔行扫描gif进行渲染
            multipass: true //类型: Boolean
            //默认: false多次优化svg直到完全优化
        }))
        .pipe(gulp.dest(_output))
}

// es7 => es5 这里使用babel7
function ES6() {
    let es6Src = `${files['main']}${files['src']['es6']}**/*.js`;
    let _output = `${files['main']}js`
    return gulp.src(es6Src)
        .pipe(babel())
        .on('error', swallowError)
        .pipe(browserify())
        .pipe(uglify())
        .pipe(gulp.dest(_output))
        .pipe(connect.reload())
}

function ES6Pro() {
    let es6Src = `${files['main']}${files['src']['es6']}**/*.js`;
    let _output = `${files['outPut']}js`
    let revOutput = `${files['main']}rev/js`;

    return gulp.src(es6Src)
        .pipe(babel())
        .pipe(browserify())
        .pipe(uglify())
        .pipe(rev()) // 生成版本映射
        .pipe(gulp.dest(_output))
        .pipe(rev.manifest()) // 生成映射文件
        .pipe(gulp.dest(revOutput))
}

// ts => es5
function tsES5() {
    let tsSrc = `${files['main']}${files['src']['ts']}**/*.ts`;
    let _output = `${files['main']}js`
    return gulp.src(tsSrc)
        .pipe(tsConfig())
        .on('error', swallowError)
        .pipe(gulp.dest(_output))
        .pipe(connect.reload())
}

function tsES5Pro() {
    let tsSrc = `${files['main']}${files['src']['ts']}**/*.ts`;
    let _output = `${files['outPut']}js`
    let revOutput = `${files['main']}rev/js`;
    return gulp.src(tsSrc)
        .pipe(tsConfig())
        .on('error', swallowError)
        .pipe(browserify())
        .pipe(uglify())
        .pipe(rev()) // 生成版本映射
        .pipe(gulp.dest(_output))
        .pipe(rev.manifest()) // 生成映射文件
        .pipe(gulp.dest(revOutput))
}

function watchFile(cb) {
    let mainSrc = files['main'];
    let filesSrc = files['src'];
    let itemName;
    let item;

    //监听files['main'] 任何文件变化,实时刷新
    for (let i in filesSrc) {
        itemName = i;
        item = filesSrc[i];

        if (itemName !== 'js' && itemName !== 'style' && itemName !== 'ts') {
            watch([`${mainSrc}${filesSrc[itemName]}**/*.${itemName}`], gulp.series(reload)).pipe(connect.reload())
        } else {
            //js和scss因为需要转译 所以另行处理
            if (itemName === 'js') {
                watch([`${mainSrc}${filesSrc['es6']}**/*.js`], gulp.series(ES6))
            } else if (itemName === 'style') {
                watch([`${mainSrc}${filesSrc['style']}**/*.scss`], gulp.series(style))
            } else if (itemName === 'ts') {
                watch([`${mainSrc}${filesSrc['ts']}**/*.ts`], gulp.series(tsES5))
            }
        }
    }

    cb();
}

gulp.task(html);

gulp.task(cleanFile);

gulp.task(stylePro);

gulp.task(createFile);

gulp.task(watchFile);

gulp.task(reload);

gulp.task(imgMini);

// 生产环境
gulp.task('pro', gulp.series(cleanFile, stylePro, html, imgMini, ES6Pro, tsES5Pro));

// 开发环境
gulp.task('dev', gulp.series(watchFile, devSever, openBrowser))

// gulp 命令后面不带参数 默认执行default
gulp.task('default', async (cb) => {
    console.log('gulp 默认任务');
    cb()
});